package lumis.portal.propertybag;

import java.util.Collections;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import lumis.portal.manager.ManagerFactory;
import lumis.portal.stability.StableMinor;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.GenericGenerator;

/**
 * Represents a property of a {@link PropertyBag}.
 * <p>
 * A property has a name, a value that may be a string or array of strings,
 * and a flag that indicates whether the property is read-only or not.
 * A read-only property may not be personalized.
 * </p>
 *
 * @stereotype entity
 * 
 * @version $Revision: 13082 $ $Date: 2011-05-27 16:03:12 -0300 (Fri, 27 May 2011) $
 * @since 5.0.0
 */
@StableMinor(version = "6.0", sinceVersion = "5.0")
@Entity
@Table(name="lum_PBProperty")
@GenericGenerator(name="portal-guid", strategy="lumis.portal.dao.hibernate.GuidGenerator")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Property
{
	@Id @GeneratedValue(generator="portal-guid")
	private String id;
	
	private String name;
	
	private boolean isPrivate;
	
	@OneToOne(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
	@JoinColumn(name="propertyDefaultId")
	private PropertyDefault propertyDefault;
	
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="propertyBagId")
	private PropertyBag propertyBag;
	
	Property()
	{
	}
	
	/**
	 * Creates a property.
	 * 
	 * @param name the name for the property.
	 * @since 5.0.0
	 */
	Property(String name)
	{
		this.name = name;
	}
	
	/**
	 * Returns the identifier of this property.
	 * 
	 * @return identifier of this property.
	 * @since 5.0.0
	 */
	public String getId()
	{
		return id;
	}
	
	/**
	 * Returns the name of this property.
	 * 
	 * @return the name of this property.
	 * @since 5.0.0
	 */
	public String getName()
	{
		return name;
	}
	
	/**
	 * Returns the list of default values stored for this property. Does not
	 * take inheritance into account. If this property is inheriting its default
	 * values, <code>null</code> is returned.
	 * 
	 * @return the list of default values. Returns an empty list if this 
	 * property is set to have no default values, or returns <code>null</code>
	 * if this property inherits its default values from a parent property bag.
	 * 
	 * @since 5.0.0
	 */
	public List<String> getDefaultValues()
	{
		if (propertyDefault != null)
			return propertyDefault.getValues();
		else
			return null;
	}
	
	/**
	 * Sets the default values for this property.
	 * 
	 * @param values the list of values to set, an empty list to set this 
	 * property to have explicitly no value, 
	 * or <code>null</code> to indicate this property will inherit its default 
	 * values.
	 * @since 5.0.0
	 */
	public void setDefaultValues(List<String> values)
	{
		if (values != null)
		{
			// set values in propertyDefault
			if (propertyDefault == null)
				propertyDefault = new PropertyDefault();
			propertyDefault.setValues(values);
		}
		else
		{
			// remove propertyDefault if it exists
			if (propertyDefault != null)
			{
				ManagerFactory.getEntityManager().remove(propertyDefault);
				propertyDefault = null;
			}
		}
	}
	
	/**
	 * Returns the parent property bag of this property.
	 * 
	 * @return the parent property bag.
	 * @since 5.0.0
	 */
	public PropertyBag getPropertyBag()
	{
		return propertyBag;
	}
	
	/**
	 * Sets the parent property bag of this property.
	 * This is a package level method.
	 * 
	 * @param propertyBag the parent property bag.
	 * @since 5.0.0
	 */
	void setPropertyBag(PropertyBag propertyBag)
	{
		this.propertyBag = propertyBag;
	}
	
	/**
	 * Returns the values for this property, applying all property values
	 * evaluation rules.
	 * @return the values, or an empty list if this property has no value.
	 * @since 5.0.0
	 */
	public List<String> getValues()
	{
		List<String> defaultValues = getDefaultValues();
		if (defaultValues == null)
		{
			// get property inherited values
			PropertyBag parentBag = getPropertyBag().getParentPropertyBag();
			if (parentBag != null)
				return parentBag.getValues(getName(), Collections.<String>emptyList());
			else
				return Collections.emptyList();
		}
		else
		{
			return defaultValues;
		}
	}
	
	/**
	 * Specifies if this property is private or public.
	 * By default private properties should not be directly exposed to the end user.
	 * 
	 * @param isPrivate true if the property is private.
	 * @since 5.0.0
	 */
	public void setPrivate(boolean isPrivate)
	{
		this.isPrivate = isPrivate;
	}
	
	/**
	 * Returns whether the property is private or not.
	 * 
	 * @return true if the property is private.
	 * @since 5.0.0
	 */
	public boolean isPrivate()
	{
		return isPrivate;
	}
}
